package zdream.pmfield.test.stage.ability;

import org.junit.Before;
import org.junit.Test;

import java.util.Objects;

import zdream.pmfield.data.foe.Ability;
import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.data.foe.MoveTemplate;
import zdream.pmfield.data.foe.Type;
import zdream.pmfield.stage.StageBuilderForTest;
import zdream.pmfield.stage.StageWrapper;
import zdream.pmfield.stage.message.ActionStartMessage;
import zdream.pmfield.stage.message.AttackMessage;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.TurnFinishedMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.storage.FoeTemplateStorage;
import zdream.pmfield.test.utils.ResourceConstant;
import zdream.pmfield.test.utils.StageConstant;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-12-18
 * @since 0.0.1
 */
public class SpeciesAbilitiesCase {
	FoeTemplateStorage storage;

	@Before
	public void before() {
		storage = new FoeTemplateStorage();
		storage.load();
		storage.loadMovesInResource(ResourceConstant.RES_PATH_TEST_MOVES_JSON);
	}

	@Test
	public void testDragonSpecies() {
		MoveTemplate normalMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", normalMove.name);
		MoveTemplate electricMove = storage.findMoveById(10000108, 1);
		assertEquals("电", electricMove.name);
		MoveTemplate iceMove = storage.findMoveById(10000109, 1);
		assertEquals("冰", iceMove.name);
		MoveTemplate lightMove = storage.findMoveById(10000111, 2);
		assertEquals("光", lightMove.name);

		final Ability ability = storage.findAbilityById(10002, 1);
		assertEquals("龙族", ability.name);

		Foe foe1 = Foe.builder(storage.findFoeById(142))
				.withLevel(30)
				.withMove(normalMove)
				.withMove(electricMove)
				.withMove(iceMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(147))
				.withLevel(30)
				.withMove(lightMove)
				.withAbility(ability)
				.skipShuffleVStat()
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		assertTrue(wrapper.stage.findFoe(foe2).hasBuff(StageConstant.ABILITY_DRAGON_GROUP));
		assertArrayEquals(new Type[] {Type.LIGHT}, foe2.template.types);
		System.out.println("TEST 1: 龙族防电");

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest();
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.onActionRequest(
				wrapper.createDefaultController().withMove(electricMove),
				foe1);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe1, message.action.asAttack().foe.foe);
			assertEquals(electricMove, message.action.asAttack().move.move);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe1, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed < 1);
			return true;
		});

		System.out.println("TEST 2: 龙族不防冰");
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);

		wrapper.onActionRequest(
				wrapper.createDefaultController().withMove(iceMove),
				foe1);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe1, message.action.asAttack().foe.foe);
			assertEquals(iceMove, message.action.asAttack().move.move);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertTrue(message.report.action.isForAttack());
			assertEquals(foe1, message.report.action.asAttack().foe.foe);
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed > 1);
			return true;
		});

		System.out.println("TEST 3: 龙族攻击没有本系加成");
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);
		wrapper.onActionRequest(
				wrapper.createDefaultController().withMove(lightMove),
				foe2);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe2, message.action.asAttack().foe.foe);
			assertEquals(lightMove, message.action.asAttack().move.move);
			return true;
		});
		wrapper.runUntil(RollReportMessage.class, message -> {
			assertEquals(StageConstant.ROLL_TITLE_ACTION, message.title);
			assertEquals(1, message.moreRolls.length);
			assertTrue(message.moreRolls[0].createdBy.is(StageConstant.BUFF_TYPE));
			assertNotNull(message.moreRolls[0].affectedBy);
			assertTrue(message.moreRolls[0].affectedBy.is(StageConstant.ABILITY_DRAGON_GROUP));
			assertFalse(message.moreRolls[0].affectResult);
			assertFalse(message.moreRolls[0].result);
			return true;
		});
	}

	@Test
	public void testBugAndBirdSpecies() {
		MoveTemplate pureMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", pureMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000100, 1);
		assertEquals("普", normalMove.name);
		MoveTemplate grassMove = storage.findMoveById(10000101, 1);
		assertEquals("草", grassMove.name);
		MoveTemplate fireMove = storage.findMoveById(10000102, 1);
		assertEquals("火", fireMove.name);
		MoveTemplate windMove = storage.findMoveById(10000104, 1);
		assertEquals("风", windMove.name);
		MoveTemplate darkMove = storage.findMoveById(10000110, 1);
		assertEquals("恶", darkMove.name);

		assertTrue(normalMove.mainType() == Type.NORMAL);
		assertTrue(grassMove.mainType() == Type.GRASS);
		assertTrue(fireMove.mainType() == Type.FIRE);
		assertTrue(windMove.mainType() == Type.WIND);
		assertTrue(darkMove.mainType() == Type.DARK);

		final Ability bug = storage.findAbilityById(10003, 1);
		assertEquals("虫族", bug.name);
		final Ability bird = storage.findAbilityById(10004, 1);
		assertEquals("鸟族", bird.name);

		Foe foe1 = Foe.builder(storage.findFoeById(12))
				.withLevel(30)
				.withMove(pureMove)
				.withMove(darkMove)
				.withMove(normalMove)
				.withAbility(bug)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(17))
				.withLevel(30)
				.withMove(pureMove)
				.withMove(grassMove)
				.withMove(fireMove)
				.withMove(windMove)
				.withAbility(bird)
				.skipShuffleVStat()
				.build();
		Foe foe3 = Foe.builder(storage.findFoeById(35))
				.withLevel(30)
				.withMove(pureMove)
				.skipShuffleVStat()
				.build();

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2, foe3)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		final FoeActor foe1Actor = wrapper.stage.findFoe(foe1);
		final FoeActor foe2Actor = wrapper.stage.findFoe(foe2);

		assertTrue(foe1Actor.hasBuff(StageConstant.ABILITY_BUG_GROUP));
		assertTrue(foe2Actor.hasBuff(StageConstant.ABILITY_BIRD_GROUP));
		System.out.println("TEST 1: 鸟族自动送飞行技能");

		assertTrue(foe2Actor.stageMoves.stream().anyMatch(move -> Objects.equals(move.move.name, "起飞")));
		assertTrue(foe2Actor.stageMoves.stream().anyMatch(move -> Objects.equals(move.move.name, "落地")));

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest();
		System.out.println("TEST 2: 虫族用恶系技能打鸟, 效果一般");
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(darkMove).withTargetFoe(foe2), foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe1, message.action.asAttack().foe.foe);
			assertEquals(darkMove, message.action.asAttack().move.move);
			assertEquals(foe2, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed < 1);
			return true;
		});

		System.out.println("TEST 3: 虫族用普通系技能打鸟, 效果正常");
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(normalMove).withTargetFoe(foe2), foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe1, message.action.asAttack().foe.foe);
			assertEquals(normalMove, message.action.asAttack().move.move);
			assertEquals(foe2, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertEquals(0, 1, message.report.items.get(0).typeFixed);
			return true;
		});

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		System.out.println("TEST 4: 虫族防草");
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(grassMove), foe2);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe2, message.action.asAttack().foe.foe);
			assertEquals(grassMove, message.action.asAttack().move.move);
			assertEquals(foe1, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed < 1);
			return true;
		});

		System.out.println("TEST 5: 虫族不防火");
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(fireMove), foe2);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe2, message.action.asAttack().foe.foe);
			assertEquals(fireMove, message.action.asAttack().move.move);
			assertEquals(foe1, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed > 1);
			return true;
		});

		System.out.println("TEST 6: 虫族不防风");
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(windMove), foe2);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe2);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe2, message.action.asAttack().foe.foe);
			assertEquals(windMove, message.action.asAttack().move.move);
			assertEquals(foe1, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed > 1);
			return true;
		});

		System.out.println("TEST 7: 虫族用恶系技能打光: 效果拔群");
		final FoeActor foe3Actor = wrapper.stage.findFoe(foe3);
		assertTrue(foe3Actor.hasBuff(Type.LIGHT.name()));

		wrapper.clearOnMessageHooks();
		wrapper.onDefaultActionRequest();
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(darkMove).withTargetFoe(foe3), foe1);
		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);
		wrapper.runUntil(ActionStartMessage.class, message -> {
			assertTrue(message.action.isForAttack());
			assertEquals(foe1, message.action.asAttack().foe.foe);
			assertEquals(darkMove, message.action.asAttack().move.move);
			assertEquals(foe3, message.action.asAttack().targetFoe.foe);
			return true;
		});
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertTrue(message.report.items.get(0).typeFixed > 1);
			return true;
		});
	}

	@Test
	public void testGhostSpecies() {
		MoveTemplate pureMove = storage.findMoveById(10000015, 1);
		assertEquals("无效果", pureMove.name);
		MoveTemplate normalMove = storage.findMoveById(10000100, 1);
		assertEquals("普", normalMove.name);

		final Ability ghost = storage.findAbilityById(10005, 1);
		assertEquals("幽灵族", ghost.name);

		Foe foe1 = Foe.builder(storage.findFoeById(84))
				.withLevel(30)
				.withMove(pureMove)
				.withMove(normalMove)
				.skipShuffleVStat()
				.build();
		Foe foe2 = Foe.builder(storage.findFoeById(93))
				.withLevel(30)
				.withMove(pureMove)
				.withAbility(ghost)
				.skipShuffleVStat()
				.build();
		assertArrayEquals(new Type[] {Type.MIND}, foe2.template.types);

		final StageWrapper wrapper = StageBuilderForTest.builder()
				.putFoeToOneSide(foe1)
				.putFoeToAnotherSide(foe2)
				.withStorage(storage)
				.disableDistanceCheck()
				.buildWrapper();

		wrapper.printBeforeHandleMessage();
		wrapper.onDefaultActionRequest();
		System.out.println("TEST 1: 鬼免疫普通");

		wrapper.runUntil(TurnStartMessage.class, message -> message.foe.foe == foe1);
		wrapper.onActionRequest(wrapper.createDefaultController().withMove(normalMove), foe1);
		wrapper.runUntil(AttackMessage.class, message -> {
			assertEquals(1, message.report.items.size());
			assertEquals(0, 0, message.report.items.get(0).typeFixed);
			return true;
		});
		wrapper.runUntil(TurnFinishedMessage.class);
	}
}
